import { type Dirent, promises as fs } from "fs";
import { join, resolve, basename } from "path";

export enum ItemTypeEnum {
    File = "File",
    Folder = "Folder"
}

export interface BaseProjectItem {
    /** 绝对路径 */
    absolutePath: string;
    /** 名称 */
    name: string;
    /** 类型 */
    type: ItemTypeEnum;
    /** 概述信息 */
    abstract?: string;
    /** 旧概述信息 */
    oldAbstract?: string;
    /** 错误 */
    error?: {
        /** 错误信息 */
        message: string;
        /** 跳过错误，默认 false */
        skip?: boolean;
    };
}

export interface ProjectItemFile extends BaseProjectItem {
    type: ItemTypeEnum.File;
}

export interface ProjectItemFolder extends BaseProjectItem {
    type: ItemTypeEnum.Folder;
    subItems: ProjectItem[];
}

export type ProjectItem = ProjectItemFile | ProjectItemFolder;

export class ProjectProcessor {
    /**
     * 获取目录下所有文件和文件夹
     * @param path 路径
     * @returns 文件和文件夹列表
     */
    static async getFilesFromPath(path: string) {
        try {
            return await fs.readdir(path, { withFileTypes: true, recursive: false });
        } catch (error) {
            throw new Error(`无法读取目录: ${(error as Error).message}`);
        }
    }
    /**
     * 获取目录结构
     * @param path 项目路径
     * @returns 目录结构
     */
    static async getDirectory(path: string): Promise<ProjectItem[]> {
        try {
            const items: ProjectItem[] = [];
            const absolutePath = resolve(path);

            // 根目录
            const rootItem: ProjectItemFolder = {
                absolutePath,
                name: basename(path),
                type: ItemTypeEnum.Folder,
                subItems: []
            };
            items.push(rootItem);
            const stack: { item: ProjectItemFolder; files: Dirent[] }[] = [
                { item: rootItem, files: await ProjectProcessor.getFilesFromPath(absolutePath) }
            ];

            while (stack.length > 0) {
                const { item, files } = stack.shift()!;

                // 目录为空则跳过
                if (files.length === 0) continue;

                await Promise.all(
                    files.map(async (file) => {
                        const fileAbsolutePath = join(item.absolutePath, file.name);
                        if (file.isDirectory()) {
                            const subItem: ProjectItemFolder = {
                                absolutePath: fileAbsolutePath,
                                name: file.name,
                                type: ItemTypeEnum.Folder,
                                subItems: []
                            };
                            item.subItems.push(subItem);

                            // 读取子目录并推入栈
                            const subFiles =
                                await ProjectProcessor.getFilesFromPath(fileAbsolutePath);
                            stack.push({ item: subItem, files: subFiles });
                        } else {
                            const subItem: ProjectItemFile = {
                                absolutePath: fileAbsolutePath,
                                name: file.name,
                                type: ItemTypeEnum.File
                            };
                            // 添加文件到当前目录
                            item.subItems.push(subItem);
                        }
                    })
                );
            }

            return items;
        } catch (error) {
            throw new Error(`无法读取目录: ${(error as Error).message}`);
        }
    }

    static async traverseDirectory(
        pathOrItems: string | ProjectItem[],
        itemCallback: (item: ProjectItem) => Promise<ProjectItem> | ProjectItem
    ): Promise<ProjectItem[]> {
        let projectItems: ProjectItem[];
        if (typeof pathOrItems === "string") {
            projectItems = await ProjectProcessor.getDirectory(pathOrItems);
        } else {
            projectItems = pathOrItems;
        }

        if (projectItems.length === 0) return [];

        const resultItems: ProjectItem[] = [];
        const folderStack: ProjectItemFolder[] = [];

        while (projectItems.length > 0) {
            const item = projectItems.pop()!;

            const lastStackFolder = folderStack[folderStack.length - 1];

            if (item.type === ItemTypeEnum.File) {
                const result = await itemCallback(item);
                // const result = item;

                if (lastStackFolder && lastStackFolder.subItems) {
                    const index = lastStackFolder.subItems.findIndex((subItem) => subItem === item);
                    if (index !== -1) {
                        lastStackFolder.subItems[index] = result;
                    }
                } else {
                    resultItems.push(result);
                }
            } else if (item.type === ItemTypeEnum.Folder) {
                if (lastStackFolder && lastStackFolder === item) {
                    const result = await itemCallback(folderStack.pop()!);
                    // const result = folderStack.pop()!;

                    const lastStackFolder2 = folderStack[folderStack.length - 1];
                    if (!lastStackFolder2) {
                        resultItems.push(result);
                        continue;
                    }
                    const index = lastStackFolder2.subItems.findIndex(
                        (subItem) => subItem === item
                    );
                    if (index !== -1) {
                        lastStackFolder2.subItems[index] = result;
                    }
                    // projectItems.pop();
                } else {
                    folderStack.push(item);
                    projectItems.push(item);
                    projectItems.push(...item.subItems);
                }
            }
        }

        return resultItems;
    }
}
